home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 April: Mac OS SDK / Dev.CD Apr 97 SDK1.toast / Development Kits (Disc 1) / Open Transport / Sample Code / DLPI / OT Mentat DLPI Template / loopback.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-11-19  |  18.8 KB  |  722 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        loopback.c
  3.  
  4.     Contains:     * Simple loopback driver utilizing Mentat DLPI Template Code (dlpiether.c)
  5.                  * This file, combined with dlpiether.c (or linked against the Shared LIbrary
  6.                  * containing dlpiether.c) is a functioning loopback driver for Open Transport.
  7.                  * This file also serves as a template for writing the hardware-specific
  8.                  * code for a driver which will use dlpiether.c.  Comments in the code and
  9.                  * the accompanying documentation, Mentat DLPI Driver Template, provide
  10.                  * necessary information.
  11.  
  12.              ** loopback.c 1.2 Last Changed 29 Mar 1996
  13.  
  14.     Copyright:    © 1996 by Mentat Inc.
  15.  
  16.     To Do:
  17. */
  18.  
  19. static char    sccs_loop_id[] = "@(#)loopback.c    1.2";
  20. #define ulong    unsigned long
  21. #define uint    unsigned long
  22. /*
  23.  * "BoardX:" in comments refers to a typical hardware driver.  These are hints
  24.  * and suggestions for converting this code to an actual hardware driver.
  25.  */
  26. #include <OpenTptModule.h>
  27. #include <dlpi.h>
  28. #include "Devices.h"
  29. #include <NameRegistry.h>
  30. #include <OpenTptLinks.h>
  31. #include <OpenTptPCISupport.h>
  32. #include <dlpiuser.h>
  33. #include <dlpiether.h>
  34.  
  35. /*
  36.  * The following defines are inserted here for convenience for the 
  37.  * loopback driver.  A "real" driver may place these in separate header
  38.  * files and/or use system-defined values.
  39.  */
  40. #define LOOPBACK_DRIVER        /* conditionally compiles loopback code */
  41.  
  42. /* Some useful MacBug debugging macros */
  43.  
  44. #define mps_printf        OTKernelPrintfForMentat
  45. #define loop_error(a)        mps_printf a
  46. #define loop_trace(a)        mps_printf a
  47. #define loop_debug(a)        if(loop_debug) {mps_printf a;} else {;}
  48. #define loop_debug2(a)        if(loop_debug >= 2) {mps_printf a;} else {;}
  49. #define    trace_args    "  "        
  50. #define    trace0        "0 "    /* Fatal */
  51. #define    trace1        "1 "    /* General */
  52. #define    trace2        "2 "    /* Out of Memory */
  53. #define    trace3        "3 "    /* Event */
  54. #define    trace_eol        
  55. extern    int    OTKernelPrintfForMentat(char * fmt, ...);
  56. static    int    loop_debug = 1;
  57.  
  58. #define    MAX_PACKET_SIZE    1500    /* We're ethernet, remember :-) */
  59. #define    MIN_PACKET_SIZE    60    /* We'll pad short packets Ethernet style */
  60. #define LOOP_HIWAT    2048    /* Flow control high water mark */
  61. #define LOOP_LOWAT    64    /* Flow control low water mark */
  62.  
  63. /* just so DL_INFO_ACK has something to report */
  64. static    unsigned char    my_hardware_addr[] = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
  65.  
  66. /*
  67.  * See OpnTptLinks.h for more
  68.  */
  69. #define kLoopbackModuleID    7199        /* STREAMS module_info mi_idnum */
  70. #define kLoopbackName        "loopback"    /* mi_idname */
  71.  
  72.  
  73. /* Driver information 'per board', e.g., internal control structure. */
  74. typedef struct board_s {
  75.     dle_t    board_dle;        /* Must be first in structure. */
  76.     /* BoardX: Fields needed for controlling hardware go here */
  77. } board_t;
  78.  
  79. /* 
  80.  * STREAMS instance data (q_ptr) 
  81.  */
  82. typedef struct loop_s {
  83.     dcl_t    loop_dcl;        /* Must be first in structure */
  84.     /* BoardX: Any 'per stream' fields needed by board go here */
  85. } loop_t;
  86. #define    loop_dle    loop_dcl.dcl_hw        /* pointer to board_t structure */
  87.  
  88. static    void    loop_bcopy_to_noncached(unsigned char * src, unsigned char * dst
  89.             , unsigned int count);
  90. static    int    loop_close(queue_t * q, int flag, cred_t * credp);
  91. static    void    loop_hw_address_filter_reset(void * loopvp, dle_addr_t * addr_list
  92.             , ulong addr_count, ulong promisc_count
  93.             , ulong multi_promisc_count, ulong accept_broadcast
  94.             , ulong accept_error);
  95. static    void    loop_hw_start(void * loopvp);
  96. static    void    loop_hw_stop(void * loopvp);
  97. static    int    loop_open(queue_t * q, dev_t * devp, int flag, int sflag
  98.             , cred_t * credp);
  99. static    mblk_t    * loop_reallocb(mblk_t * mp, int new_size);
  100. static    int    loop_rsrv(queue_t * q);
  101. static    int    loop_wput(queue_t * q, mblk_t * mp);
  102. static    mblk_t    * loop_wput_process(queue_t * q, mblk_t * mp, long * total);
  103.  
  104. /*
  105.  * Global pointer to the board_t structure for this device instantiation.  This
  106.  * structure is allocated in the InitStreamModule routine and freed in
  107.  * TerminateStreamModule.  CFM guarantees us a separate data space for all
  108.  * instances of the device, so this pointer is always unique.
  109.  */
  110. static    board_t    * board_global;
  111.  
  112. /* STREAMS configuration structures. */
  113. static struct module_info info =  {
  114.     kLoopbackModuleID, kLoopbackName
  115.     , MIN_PACKET_SIZE, MAX_PACKET_SIZE
  116.     , LOOP_HIWAT, LOOP_LOWAT
  117. };
  118.  
  119. /* 
  120.  * STREAMS qinit structures.  Drivers have no rput.
  121.  */
  122. static struct qinit rinit = {
  123.     NULL, loop_rsrv, loop_open, loop_close, NULL, &info
  124. };
  125.  
  126. static struct qinit winit = {
  127.     loop_wput, NULL, loop_open, loop_close, NULL, &info
  128. };
  129.  
  130. struct streamtab loopback_info = { &rinit, &winit };
  131.  
  132. /* 
  133.  * BoardX:  Hardware drivers would typically add 'kOTModUsesInterrupts' to
  134.  * flags entry of following.
  135.  */
  136. static install_info    loopback_install_info =
  137. {
  138.     &loopback_info,
  139.     kOTModIsDriver | kOTModUpperIsDLPI,
  140.     SQLVL_MODULE,
  141.     NULL,
  142.     0,
  143.     0
  144. };
  145.  
  146. #ifndef LOOPBACK_DRIVER
  147. static void
  148. board_bcopy_to_noncached (unsigned char * src, unsigned char * dst, unsigned int count)
  149. {
  150.     unsigned int iterations;
  151.  
  152.     /* Do short copies with a simple loop. */
  153.     if (count <= 15) {
  154.         do {
  155.             --count;
  156.             *dst++ = *src++;
  157.         } while (count);
  158.         return;
  159.     }
  160.  
  161.     /* Align on 8 byte dst boundary */
  162.     iterations = (8 - ((unsigned int)dst & 0x7)) & 0x7;
  163.     count -= iterations;
  164.     if (iterations) {
  165.         do {
  166.             --iterations;
  167.             *dst++ = *src++;
  168.         } while (iterations);
  169.     }
  170.  
  171.     /* Copy 32 byte chunks */
  172.     iterations = count >> 5;
  173.     count &= 0x1F;
  174.     if (iterations) {
  175.         double * dsrc = (double *)src;
  176.         double * ddst = (double *)dst;
  177.         do {
  178.             iterations--;
  179.             ddst[0] = dsrc[0];
  180.             ddst[1] = dsrc[1];
  181.             ddst[2] = dsrc[2];
  182.             ddst[3] = dsrc[3];
  183.             ddst += 4;
  184.             dsrc += 4;
  185.         } while (iterations);
  186.         src = (unsigned char *)dsrc;
  187.         dst = (unsigned char *)ddst;
  188.     }
  189.  
  190.     /* Copy 4 byte chunks */
  191.     iterations = count >> 2;
  192.     count &= 0x3;
  193.     if (iterations) {
  194.         unsigned long * lsrc = (unsigned long *)src;
  195.         unsigned long * ldst = (unsigned long *)dst;
  196.         do {
  197.             --iterations;
  198.             *ldst++ = *lsrc++;
  199.         } while (iterations);
  200.         src = (unsigned char *)lsrc;
  201.         dst = (unsigned char *)ldst;
  202.     }
  203.  
  204.     /* Copy the rest */
  205.     while (count--)
  206.         *dst++ = *src++;
  207. }
  208.  
  209. /*
  210.  * Template interrupt routine for processing inbound packets.
  211.  */
  212. long board_intr ()
  213. {
  214.     board_t    * board = board_global;
  215.     void    * cookie;
  216.     dle_t    * dle = &board->board_dle;
  217.     mblk_t    * mp;
  218.  
  219.     OTEnterInterrupt();
  220.  
  221.     /* Handle transmit done interrupts, if necessary. */
  222.  
  223.     /* Handle receive interrupts. */
  224.     while (receive_packets_available) {
  225.         /* Perhaps use esballoc() to avoid data copy? */
  226.         mp = allocb(packet_length, BPRI_LO);
  227.         if (!mp) {
  228.             dle->dle_istatus.receive_discards++;
  229.             continue;
  230.         }
  231.  
  232.         /* Copy the data into mp->b_rptr. */
  233.  
  234.         mp->b_wptr = mp->b_rptr + packet_length;
  235.  
  236.         /* Deliver the packet. */
  237.         dle_inbound(dle, mp);
  238.     }
  239.  
  240.     OTLeaveInterrupt();
  241.     return 1;
  242. }
  243. #endif    /* LOOPBACK_DRIVER */
  244.  
  245. /* This function must be exported; see loop.exp */
  246. install_info *
  247. GetOTInstallInfo ()
  248. {
  249.     loop_debug((trace_args "GetOTInstallInfo" trace_eol));
  250.     return &loopback_install_info;
  251. }
  252.  
  253. Boolean
  254. InitStreamModule (void * systemDependent)
  255. {
  256.     board_t    * board;
  257.     dle_t    * dle;
  258.  
  259.     loop_debug((trace_args "InitStreamModule" trace_eol));
  260.  
  261.     /* Allocate the board_t structure for this device */
  262.     board = (board_t *)OTAllocMem(sizeof(board_t));
  263.     if (!board)
  264.         return false;
  265.  
  266.     /* These are the hardware start/stop/reset functions */
  267.     bzero((char *)board, sizeof(board_t));
  268.     dle = &board->board_dle;
  269.     dle->dle_hw.dlehw_start = loop_hw_start;
  270.     dle->dle_hw.dlehw_stop = loop_hw_stop;
  271.     dle->dle_hw.dlehw_address_filter_reset = loop_hw_address_filter_reset;
  272.     dle->dle_hw.dlehw_send_error = NULL;
  273.     dle->dle_hw.dlehw_recv_error_flags = 0;
  274.  
  275.     /*
  276.      * Suggestions:
  277.      * - Install interrupt vectors in case any memory allocations are
  278.      *    required.  Interrupts should not be enabled until the
  279.      *    board start routine is called.
  280.      * - Reset the hardware to a known state.  If the hardware does
  281.      *    not respond, then return false.
  282.      * - Read the Ethernet address from the board's ROM area.  This
  283.      *    address should be copied into both the dle_factory_addr
  284.      *    field and the dle_current_addr field.
  285.      */
  286.     bcopy(my_hardware_addr, dle->dle_factory_addr, 6);
  287.     bcopy(my_hardware_addr, dle->dle_current_addr, 6);
  288.  
  289.     /*
  290.      * Allow the DLPI common code to initialize the dle fields. Once
  291.      * dle_init is called, dle_terminate must be called before freeing
  292.      * the dle structure.  There is private memory allocated for each
  293.      * dle that needs to be freed.
  294.      */
  295.     dle_init(dle, 0);
  296.     board_global = board;
  297.  
  298.     return true;
  299. }
  300.  
  301. void
  302. TerminateStreamModule (void)
  303. {
  304.     board_t    * board = board_global;
  305.  
  306.     loop_debug((trace_args "TerminateStreamModule" trace_eol));
  307.  
  308.     /*
  309.      * Suggestions:
  310.      * - Remove interrupt vectors.
  311.      * - Reset the hardware to a known state.
  312.      */
  313.  
  314.     board_global = (board_t *)NULL;
  315.     dle_terminate(&board->board_dle);
  316.     OTFreeMem(board);
  317. }
  318.  
  319. OTResult
  320. ValidateHardware (RegEntryID * our_id)
  321. {
  322.     loop_debug((trace_args "ValidateHardware" trace_eol));
  323.     return kOTNoError;    
  324. }
  325.  
  326. /* STREAMS close routine. */
  327. static int
  328. loop_close (queue_t * q, int flag, cred_t * credp)
  329. {
  330.     loop_debug((trace_args "loop_close(%x), dcl_dle %x" trace_eol
  331.         , q, ((dcl_t *)q->q_ptr)->dcl_hw ));
  332.     /*
  333.      * NOTE: there is probably not much else that needs to be done
  334.      * in this routine.  If you need to know about the number of
  335.      * open instances, dle_refcount is the number of streams still
  336.      * referencing the device (decremented in dle_close).
  337.      */
  338.     return dle_close(q);
  339. }
  340.  
  341. static void
  342. loop_hw_address_filter_reset (void * boardvp, dle_addr_t * addr_list
  343.             , ulong addr_count, ulong promisc_count
  344.             , ulong multi_promisc_count, ulong accept_broadcast
  345.             , ulong accept_error)
  346. {
  347.     board_t        * board = boardvp;
  348.     dle_t        * dle = &board->board_dle;
  349.     dle_addr_t    * dlea;
  350.     unsigned char    * first_phys_addr = 0;
  351.     uint        phys_addr_count = 0;
  352.  
  353.     /* Calculate the new logical address filter for multicast addresses. */
  354.     for (dlea = addr_list; dlea; dlea = dlea->dlea_next) {
  355.         if (dlea->dlea_addr[0] & 0x1) {
  356.             /*
  357.              * If the address is a multicast address, then set
  358.              * the right bits in the new filter.
  359.              */
  360.         } else {
  361.             /*
  362.              * Additional physical address.  This does not happen
  363.              * with the first version of the DLPI template code.
  364.              * However, at some future time, we may want to
  365.              * support multiple physical addresses on one
  366.              * hardware tap.
  367.              */
  368.             if (!first_phys_addr)
  369.                 first_phys_addr = dlea->dlea_addr;
  370.             phys_addr_count++;
  371.         }
  372.     }
  373.  
  374.     if (first_phys_addr) {
  375.         /*
  376.          * If there were any physical, non-multicast addresses in
  377.          * the list, then check to see if the first one is different
  378.          * from the current one.  If so, copy the new address into
  379.          * dle_current_addr and take whatever steps are necessary
  380.          * with the hardware to change the physical address.
  381.          */
  382.         ;
  383.     }
  384.  
  385.     /*
  386.      * Compare the new address filter with the old one.  If the new
  387.      * one is different, then set the hardware appropriately.
  388.      */
  389.  
  390.     /*
  391.      * If there are multiple physical addresses, then the board
  392.      * probably needs to be put in a promiscuous state.
  393.      */
  394.     if (phys_addr_count > 1)
  395.         promisc_count |= 1;
  396.  
  397.     if (promisc_count || multi_promisc_count)
  398.         /* Set the board into promiscuous mode. */;
  399.     else
  400.         /* Clear any promiscuous mode that may be set*/;
  401.     /*
  402.      * Save the promiscuous setting in the board structure so that
  403.      * updates to the hardware registers from loop_start or other
  404.      * routines will be correct.
  405.      */
  406. }
  407.  
  408. /* Called by dlpiether code through dlehw_start. */
  409. static    void
  410. loop_hw_start (void * boardvp)
  411. {
  412.     board_t    * board = boardvp;
  413.  
  414.     /* Kick the board alive and allow receive interrupts. */
  415. }
  416.  
  417. /* Called by dlpiether code through dlehw_stop. */
  418. static    void
  419. loop_hw_stop (void * boardvp)
  420. {
  421.     /* Turn off interrupts and leave the hardware disabled. */
  422. }
  423.         
  424.  
  425. /* STREAMS open routine. */
  426. static    int
  427. loop_open (queue_t * q, dev_t * devp, int flag, int sflag, cred_t * credp)
  428. {
  429.     int    ret_code;
  430.  
  431.     loop_debug((trace_args "loop_open()" trace_eol));
  432.     /*
  433.      * This routine probably does not need to do anything other
  434.      * than call dle_open.  dle_refcnt is incremented.
  435.      */
  436.     ret_code = dle_open(&board_global->board_dle, q, devp, flag, sflag, credp
  437.             , sizeof(loop_t));
  438.     loop_debug((trace_args "dle_open(%x) = %d, dcl_hw %x" trace_eol
  439.         , &board_global->board_dle
  440.         , ret_code
  441.         , ((dcl_t *)q->q_ptr)->dcl_hw ));
  442.     return ret_code;
  443. }
  444.  
  445. /*
  446.  * Function used only by loopback driver; allocate or re-allocate
  447.  * a message block to assure minimum size.
  448.  */
  449. #ifdef LOOPBACK_DRIVER
  450. static mblk_t *
  451. loop_reallocb (mblk_t * mp, int new_size)
  452. {
  453.     mblk_t        * mp1;
  454.     dblk_t        * db;
  455.     int        our_size;
  456.     int        need;
  457.     unsigned char    * base;
  458.  
  459.     our_size = mp->b_wptr - mp->b_rptr;
  460.     need = (new_size > our_size) ? new_size : our_size;
  461.     db = mp->b_datap;
  462.     base = db->db_base;
  463.  
  464.     if (db->db_ref == 1) {
  465.         if ((db->db_lim - mp->b_rptr) >= need)
  466.             return mp;
  467.         if ((db->db_lim - base) >= need) {
  468.             BlockMoveData((char *)mp->b_rptr, (char *)base, our_size);
  469.             mp->b_rptr = base;
  470.             mp->b_wptr = base + our_size;
  471.             return mp;
  472.         }
  473.     }
  474.     mp1 = allocb(need, BPRI_MED);
  475.     if (mp1) {
  476.         mp1->b_wptr = mp1->b_rptr + our_size;
  477.         mp1->b_datap->db_type = db->db_type;
  478.         mp1->b_cont = mp->b_cont;
  479.         bcopy((char *)mp->b_rptr, (char *)mp1->b_rptr, our_size );
  480.     }
  481.     freeb(mp);
  482.     return mp1;
  483. }
  484. #endif    /* LOOPBACK_DRIVER */
  485.  
  486. /* 
  487.  * Read-side service routine.
  488.  * Pass all inbound packets upstream.  Messages are placed on
  489.  * the read-side queue by dle_inbound.  Unless the hardware
  490.  * requires something special, no additional code should be
  491.  * required.  NOTE: Additional messages may be added to the
  492.  * queue while this routine is running. If canputnext() fails,
  493.  * messages are freed rather than put back on the queue.  
  494.  * This is necessary since dle_inbound() will continue to add 
  495.  * messages without checking flow control (it can't call canputnext()
  496.  * from interrupt context).
  497.  */
  498. static int
  499. loop_rsrv (q)
  500.     queue_t    * q;
  501. {
  502.     mblk_t * mp;
  503.  
  504.     while (mp = getq(q)) {
  505.         /*
  506.          * Private message to be passed back to the dlpiether
  507.          * code.  This interface is required for supporting
  508.          * 802.2 XID and Test packets.
  509.          */
  510.         if (mp->b_datap->db_type == M_CTL) {
  511.             dle_rsrv_ctl(q, mp);
  512.         } else if (canputnext(q))
  513.             putnext(q, mp);
  514.         else {
  515.             freemsg(mp);
  516.             flushq(q, FLUSHDATA);
  517.             break;
  518.         }
  519.     }
  520.     return 0;
  521. }
  522.  
  523. /* Write-side put routine. Only handles M_DATA, handing others to dlpiether. */
  524. static int
  525. loop_wput (queue_t * q, mblk_t * mp)
  526. {
  527.     loop_t    * loop;
  528.     mblk_t    * first_mp;
  529.     long    len;
  530.     long    remaining;
  531.     long    total;
  532.     unsigned char    * xmt_buf;
  533.  
  534.     loop = (loop_t *)q->q_ptr;
  535.     if (mp->b_datap->db_type != M_DATA) {
  536.         mp = dle_wput(q, mp);
  537.         if (!mp)
  538.             return 0;
  539.         switch (mp->b_datap->db_type) {
  540.         case M_DATA:
  541.             break;        /* it's ready to send */
  542.         case M_IOCNAK:
  543.             /*
  544.              * Any driver private ioctl's come back from
  545.              * dle_wput() as an M_IOCNAK with ioc_error
  546.              * set to EINVAL; the rest of the original M_IOCTL
  547.              * is intact (including ioc_cmd & trailing M_DATAs).
  548.              * It may be processed here.
  549.              */
  550.              qreply(q,mp);
  551.              return 0;
  552.         default:
  553.             /* dle_wput() has formatted the reply for us */
  554.             qreply(q, mp);
  555.             return 0;
  556.         }
  557.     }
  558.  
  559.     /* Transmit the packet defined by mp->b_rptr to mp->b_wptr. */
  560.  
  561. #ifndef LOOPBACK_DRIVER
  562.     if (Can't transmit for some reason) {
  563.         ((dle_t *)loop->loop_dle)->dle_istatus.transmit_discards++;
  564.         freemsg(mp);
  565.         return 0;
  566.     }
  567.     /*
  568.      * Copy the packet to transmit buffer.  This is an example
  569.      * showing the copies and the calculation of the length
  570.      * of the packet.  Code for actual hardware devices will
  571.      * obviously need to be updated, at least to initialize
  572.      * xmt_buf to point to the hardware transmit area.
  573.      */
  574.     remaining = MAX_PACKET_SIZE;
  575.     first_mp = mp;
  576.     xmt_buf = ??;        /* BoardX: replace this */
  577.     do {
  578.         unsigned char    * rptr = mp->b_rptr;
  579.         int    len = mp->b_wptr - rptr;
  580.         if (len <= 0)
  581.             continue;
  582.         if (remaining < len) {
  583.             /* packet too large */
  584.             mp = dle_wput_ud_error(first_mp, DL_UNDELIVERABLE, 0);
  585.             if (mp)
  586.                 qreply(q, mp);
  587.             return 0;
  588.         }
  589.         remaining -= len;
  590.         xmt_buf += len;
  591.         board_bcopy_to_noncached(rptr, xmt_buf - len, len);
  592.     } while ((mp = mp->b_cont)  &&  remaining);
  593.  
  594.     total = MAX_PACKET_SIZE - remaining;
  595.  
  596.     /* Fill in the 802 length field if it is zero. */
  597.     {
  598.     unsigned char    * up;
  599.     up = &xmt_buf[-total];
  600.     if (!(up[12] | up[13])) {
  601.         uint    adjusted_total = total - 14;
  602.         up[12] = (unsigned char)(adjusted_total >> 8);
  603.         up[13] = (unsigned char)(adjusted_total & 0xff);
  604.     }
  605.     }
  606.  
  607.     if (total < MIN_PACKET_SIZE) {
  608.         /*
  609.          * If the packet is less than the minimum transmit
  610.          * size, then you need to pad the packet.  Hopefully
  611.          * this is just of matter of setting the hardware
  612.          * to pad automatically.
  613.          */
  614.          ;
  615.     }
  616.     
  617.     /* Trigger the hardware transmit. */
  618. #else
  619.     /* Loopback driver processing */
  620.     first_mp = loop_wput_process(q, mp, &total);
  621.     if (!first_mp) {
  622.         ((dle_t *)loop->loop_dle)->dle_istatus.transmit_discards++;
  623.         return 0;
  624.     }
  625.     
  626. #endif
  627.     /* Increment the appropriate MIB interface statistics. */
  628.     ((dle_t *)loop->loop_dle)->dle_istatus.bytes_sent += total;
  629.     if (first_mp->b_rptr[0] & 0x1) {
  630.         unsigned char    * rptr = first_mp->b_rptr;
  631.         if ((rptr[0] & rptr[1] & rptr[2] & rptr[3] & rptr[4]
  632.              & rptr[5]) == 0xFF)
  633.             ((dle_t *)loop->loop_dle)->dle_istatus.broadcast_frames_sent++;
  634.         else
  635.             ((dle_t *)loop->loop_dle)->dle_istatus.multicast_frames_sent++;
  636.     } else
  637.         ((dle_t *)loop->loop_dle)->dle_istatus.unicast_frames_sent++;
  638.  
  639. #ifndef LOOPBACK_DRIVER
  640.     freemsg(first_mp);
  641. #else
  642.     /*
  643.      * Loop it around - dle_inbound() call's putq(), so stack depth not
  644.      * an issue here.
  645.      */
  646.     dle_inbound((dle_t *)loop->loop_dle, first_mp);
  647.     return 0;
  648. #endif
  649. }
  650.  
  651. /*
  652.  * For the loopback driver, we turn around the message, filling in 802
  653.  * length/type field.  This function will not be used by hardware code
  654.  * modifying this template.
  655.  */
  656. #ifdef LOOPBACK_DRIVER
  657. static mblk_t *
  658. loop_wput_process (queue_t * q, mblk_t * mp, long * total)
  659. {
  660.     long        len;
  661.     long        first_len;
  662.     mblk_t        * mp1;
  663.     mblk_t        * mp2;
  664.     unsigned char    * up;
  665.  
  666.     first_len = mp->b_wptr - mp->b_rptr;
  667.     *total = first_len;
  668.     for (mp1 = mp; mp2 = mp1->b_cont; ) {
  669.         len = mp2->b_wptr - mp2->b_rptr;
  670.         if (len <= 0  ||  mp2->b_datap->db_type != M_DATA) {
  671.             mp1->b_cont = mp2->b_cont;
  672.             freeb(mp2);
  673.             continue;
  674.         }
  675.         mp1 = mp2;
  676.         *total += len;
  677.     }
  678.  
  679.     if (*total > MAX_PACKET_SIZE) {
  680.         mp = dle_wput_ud_error(mp, DL_UNDELIVERABLE, 0);
  681.         if (mp)
  682.             qreply(q, mp);
  683.         return NULL;
  684.     }
  685.     if (*total < MIN_PACKET_SIZE) {    /* we need to pad */
  686.         if (mp->b_cont && !pullupmsg(mp, -1)) {
  687.             mp = dle_wput_ud_error(mp, DL_SYSERR, ENOMEM);
  688.             if (mp)
  689.                 qreply(q, mp);
  690.             return NULL;
  691.         }
  692.         mp = loop_reallocb(mp, MIN_PACKET_SIZE);
  693.         if (!mp)
  694.             return NULL;
  695.  
  696.         bzero((char *)mp->b_wptr, (MIN_PACKET_SIZE - *total));
  697.         mp->b_wptr += (MIN_PACKET_SIZE - *total);
  698.         first_len = MIN_PACKET_SIZE;
  699.     }
  700.     /*
  701.      * db_ref note:  db_ref may be > 1 for some packets (we know some
  702.      * IP packets will have db_ref > 1).  We assume that the portion 
  703.      * of the packet we are jamming is opaque to upper levels and since
  704.      * we always write it here, things should be fine.  If db_ref > 2,
  705.      * this could be a problem, but this we know is 'never' true.
  706.      */
  707.     if (first_len < 14 && !pullupmsg(mp, 14)) {
  708.         mp = dle_wput_ud_error(mp, DL_SYSERR, ENOMEM);
  709.         if (mp)
  710.             qreply(q, mp);
  711.         return NULL;
  712.     }
  713.     up = &mp->b_rptr[12];
  714.     if (!(up[12] | up[13])) {
  715.         uint    adjusted_total = *total - 14;
  716.         up[12] = (unsigned char)(adjusted_total >> 8);
  717.         up[13] = (unsigned char)(adjusted_total & 0xff);
  718.     }
  719.     return mp;
  720. }
  721. #endif    /* LOOPBACK_DRIVER */
  722.